use apikit_expand::api_attribute::{ApiHandlerAttr, ApiModuleAttr, Protocol, ProtocolKind};
use proc_macro2::Span;
use quote::quote;
use syn::{DeriveInput, Item, LitStr, parse_quote};

#[test]
fn test_error_derive() {
    let mut input: DeriveInput = parse_quote! {
        pub enum FormRejection {
            #[error(transparent)]
            Axum(#[from] EntityError),

            #[error(transparent)]
            Validate(#[from] BadRequest),
        }
    };
    let result = apikit_expand::error_deriver::error_expand(&mut input);
    println!("{}", result.unwrap());
}

#[test]
fn test_expand_handler() {
    let input: Item = parse_quote! {
        /// hahe
        /// ws
        async fn ws(self, ws: WsHandle, params: Json<Params>) -> Result<UpgradeResponse, String>;
    };

    let result = apikit_expand::api_attribute::handler_expand(
        ApiHandlerAttr {
            name: Some(LitStr::new("ws", Span::call_site())),
            path: Some(LitStr::new("/abc", Span::call_site())),
            layers: None,
            method: None,
            tags: None,
            protocol: Some(Protocol {
                token: quote!(apikit::Protocol::Ws),
                kind: ProtocolKind::Ws,
                ty: quote!(apikit::WsProtocol),
            }),
        },
        input,
    );
    println!("{}", result.unwrap());
}

#[test]
fn test_expand_api() {
    let input: Item = parse_quote! {
        /// 模块注释
        #[async_trait::async_trait]
        pub trait Foo {
            /// hahe
            /// http
            #[get(name = "http测试", path = "/http")]
            async fn http(self, params: Json<Params>) -> Result<String, String>;

            /// hahe
            /// sse
            #[get(name = "sse测试", path = "/sse", protocol = "sse")]
            async fn sse(self, params: Json<Params>) -> Result<Event, String>;

            /// hahe
            /// ws
            #[get(name = "ws测试", path = "/ws", protocol = "ws")]
            async fn ws(self, ws: WsHandle, params: Json<Params>) -> Result<UpgradeResponse, String>;
        }
    };

    let result = apikit_expand::api_attribute::api_expand(
        ApiModuleAttr {
            name: Some(LitStr::new("Foo", Span::call_site())),
            path: Some(LitStr::new("/abc", Span::call_site())),
            layers: None,
        },
        input,
    );
    println!("{}", result.unwrap());
}

pub mod api {
    use std::convert::Infallible;

    use apikit::{
        HttpProtocol, SseProtocol, WsProtocol, handler,
        rpc::ws_client::{FromMessage, IntoMessage},
    };
    use apikit_rpc::{DutyClient, ServerAddressManager};
    use reqwest::Client;
    pub struct ServerManager;
    impl ServerAddressManager for ServerManager {
        fn get_server_addr(&self, _name: &str) -> &str {
            "http://localhost:8080"
        }
    }
    pub struct Rpc<C: DutyClient<Client, ServerManager>>(pub C);

    pub struct TypeMapper;

    pub type __TypeMapper = TypeMapper;

    pub struct WsHandle;

    pub struct Event;

    pub struct UpgradeResponse;

    impl apikit::doc::ApiRequestExtractor<Json<Params>> for __TypeMapper {
        fn extract(
            _types: &mut std::collections::HashMap<
                apikit::doc::ApiTypeId,
                apikit::doc::ApiTypeInfo,
            >,
            _request: &mut apikit::doc::ApiRequest,
        ) {
            // Here you can add the logic to extract the request parameters
        }
    }
    impl apikit::doc::ApiRequestExtractor<WsHandle> for __TypeMapper {
        fn extract(
            _types: &mut std::collections::HashMap<
                apikit::doc::ApiTypeId,
                apikit::doc::ApiTypeInfo,
            >,
            _request: &mut apikit::doc::ApiRequest,
        ) {
            // Here you can add the logic to extract the request parameters
        }
    }
    impl<T, E> apikit::doc::ApiResponseExtractor<Result<T, E>> for __TypeMapper {
        fn extract(
            _types: &mut std::collections::HashMap<
                apikit::doc::ApiTypeId,
                apikit::doc::ApiTypeInfo,
            >,
            _response: &mut apikit::doc::ApiResponse,
        ) {
            // Here you can add the logic to extract the response parameters
        }
    }

    impl<T> apikit_rpc::RequestParamSign<Json<T>> for __TypeMapper {
        type Input = String;
    }

    impl apikit_rpc::ResponseParamSign<Result<String, String>> for __TypeMapper {
        type Output = String;
    }

    pub struct CustMessage;

    impl FromMessage<reqwest_websocket::Message> for CustMessage {
        type Error = Infallible;

        fn from_message(_: reqwest_websocket::Message) -> Result<Self, Self::Error> {
            Ok(CustMessage)
        }
    }
    impl IntoMessage<reqwest_websocket::Message> for CustMessage {
        type Error = Infallible;

        fn into_message(self) -> Result<reqwest_websocket::Message, Self::Error> {
            todo!()
        }
    }

    impl apikit_rpc::WebsocketParamSign<WsHandle> for __TypeMapper {
        type SendMessage = CustMessage;
        type ReceiveMessage = CustMessage;
    }

    pub struct Json<T>(pub T);

    pub struct Params {}

    pub struct __Server;

    impl apikit::ServerDef for __Server {
        fn server_name() -> &'static str {
            "foo"
        }
    }

    fn __submit_module_fn_foo() -> apikit::doc::ApiModule {
        apikit::doc::ApiModule {
            ident: "Foo".into(),
            name: Some("Foo".into()),
            docs: Some("模块注释".into()),
            position: module_path!().to_string(),
            path: Some("/abc".into()),
            layers: vec![],
        }
    }
    inventory::submit!(apikit::doc::ApiModuleSubmitItem {
        get_module_fn: __submit_module_fn_foo
    });
    fn __submit_operation_fn_foo_http(
        types: &mut std::collections::HashMap<apikit::doc::ApiTypeId, apikit::doc::ApiTypeInfo>,
    ) -> apikit::doc::ApiOperation {
        let mut request = apikit::doc::ApiRequest::default();
        let mut response = apikit::doc::ApiResponse::default();
        <__TypeMapper as apikit::doc::ApiRequestExtractor<(Json<Params>,)>>::extract(
            types,
            &mut request,
        );
        <__TypeMapper as apikit::doc::ApiResponseExtractor<Result<String, String>>>::extract(
            types,
            &mut response,
        );
        apikit::doc::ApiOperation {
            module: Some("Foo".into()),
            position: module_path!().into(),
            ident: "http".into(),
            name: Some("http测试".into()),
            path: <FooHttpDef_ as apikit::HandlerDef>::path().into(),
            docs: Some("hahe\nhttp".into()),
            tags: vec![],
            method: apikit::Method::GET.as_str().into(),
            protocol: apikit::Protocol::Http.to_string(),
            request,
            response,
            layers: vec![],
        }
    }
    inventory::submit!(apikit::doc::ApiOperationSubmitItem {
        get_operation_fn: __submit_operation_fn_foo_http
    });
    fn __submit_operation_fn_foo_sse(
        types: &mut std::collections::HashMap<apikit::doc::ApiTypeId, apikit::doc::ApiTypeInfo>,
    ) -> apikit::doc::ApiOperation {
        let mut request = apikit::doc::ApiRequest::default();
        let mut response = apikit::doc::ApiResponse::default();
        <__TypeMapper as apikit::doc::ApiRequestExtractor<(Json<Params>,)>>::extract(
            types,
            &mut request,
        );
        <__TypeMapper as apikit::doc::ApiResponseExtractor<Result<Event, String>>>::extract(
            types,
            &mut response,
        );
        apikit::doc::ApiOperation {
            module: Some("Foo".into()),
            position: module_path!().into(),
            ident: "sse".into(),
            name: Some("sse测试".into()),
            path: <FooSseDef_ as apikit::HandlerDef>::path().into(),
            docs: Some("hahe\nsse".into()),
            tags: vec![],
            method: apikit::Method::GET.as_str().into(),
            protocol: apikit::Protocol::Sse.to_string(),
            request,
            response,
            layers: vec![],
        }
    }
    inventory::submit!(apikit::doc::ApiOperationSubmitItem {
        get_operation_fn: __submit_operation_fn_foo_sse
    });
    fn __submit_operation_fn_foo_ws(
        types: &mut std::collections::HashMap<apikit::doc::ApiTypeId, apikit::doc::ApiTypeInfo>,
    ) -> apikit::doc::ApiOperation {
        let mut request = apikit::doc::ApiRequest::default();
        let mut response = apikit::doc::ApiResponse::default();
        <__TypeMapper as apikit::doc::ApiRequestExtractor<(WsHandle, Json<Params>)>>::extract(
            types,
            &mut request,
        );
        < __TypeMapper as apikit :: doc :: ApiResponseExtractor < Result < UpgradeResponse , String > >> :: extract (types , & mut response) ;
        apikit::doc::ApiOperation {
            module: Some("Foo".into()),
            position: module_path!().into(),
            ident: "ws".into(),
            name: Some("ws测试".into()),
            path: <FooWsDef_ as apikit::HandlerDef>::path().into(),
            docs: Some("hahe\nws".into()),
            tags: vec![],
            method: apikit::Method::GET.as_str().into(),
            protocol: apikit::Protocol::Ws.to_string(),
            request,
            response,
            layers: vec![],
        }
    }
    inventory::submit!(apikit::doc::ApiOperationSubmitItem {
        get_operation_fn: __submit_operation_fn_foo_ws
    });
    pub struct FooHttpDef_;
    impl apikit::HandlerDef for FooHttpDef_ {
        type Input = (Json<Params>,);
        type Output = Result<String, String>;
        type Protocol = HttpProtocol;
        fn method() -> apikit::Method {
            apikit::Method::GET
        }
        fn path() -> &'static str {
            static PATH: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| {
                <__Server as apikit::ServerDef>::route_path(
                    Some("/http"),
                    Some("/abc"),
                    "http",
                    module_path!(),
                )
            });
            &PATH
        }
        fn protocol() -> apikit::Protocol {
            apikit::Protocol::Http
        }
    }
    pub struct FooSseDef_;
    impl apikit::HandlerDef for FooSseDef_ {
        type Input = (Json<Params>,);
        type Output = Result<Event, String>;
        type Protocol = SseProtocol;
        fn method() -> apikit::Method {
            apikit::Method::GET
        }
        fn path() -> &'static str {
            static PATH: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| {
                <__Server as apikit::ServerDef>::route_path(
                    Some("/sse"),
                    Some("/abc"),
                    "sse",
                    module_path!(),
                )
            });
            &PATH
        }
        fn protocol() -> apikit::Protocol {
            apikit::Protocol::Sse
        }
    }
    pub struct FooWsDef_;
    impl apikit::HandlerDef for FooWsDef_ {
        type Input = (WsHandle, Json<Params>);
        type Output = Result<UpgradeResponse, String>;
        type Protocol = WsProtocol;
        fn method() -> apikit::Method {
            apikit::Method::GET
        }
        fn path() -> &'static str {
            static PATH: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| {
                <__Server as apikit::ServerDef>::route_path(
                    Some("/ws"),
                    Some("/abc"),
                    "ws",
                    module_path!(),
                )
            });
            &PATH
        }
        fn protocol() -> apikit::Protocol {
            apikit::Protocol::Ws
        }
    }
    pub struct FooClient<C, S>
    where
        C: apikit::rpc::http_client::HttpClient,
        C: apikit::rpc::sse_client::SseClient,
        C: apikit::rpc::ws_client::WsClient,
        S: apikit::rpc::ServerAddressManager,
    {
        pub client: C,
        pub server_manager: S,
    }
    impl<C, S> apikit::rpc::DutyClient<C, S> for FooClient<C, S>
    where
        C: apikit::rpc::http_client::HttpClient,
        C: apikit::rpc::sse_client::SseClient,
        C: apikit::rpc::ws_client::WsClient,
        S: apikit::rpc::ServerAddressManager,
    {
        fn new(client: C, server_manager: S) -> Self {
            Self {
                client,
                server_manager,
            }
        }
    }
    impl<C, S> FooClient<C, S>
    where
        C: apikit::rpc::http_client::HttpClient,
        C: apikit::rpc::sse_client::SseClient,
        C: apikit::rpc::ws_client::WsClient,
        S: apikit::rpc::ServerAddressManager,
    {
        pub async fn http (& self , param1 : < __TypeMapper as apikit :: rpc :: RequestParamSign < Json < Params > >> :: Input ,) -> Result < < __TypeMapper as apikit :: rpc :: ResponseParamSign < Result < String , String > >> :: Output , < C as apikit :: rpc :: http_client :: HttpClient > :: Error > where < C as apikit :: rpc :: http_client :: HttpClient > :: Response : apikit :: rpc :: http_client :: FromResponse < < __TypeMapper as apikit :: rpc :: ResponseParamSign < Result < String , String > >> :: Output > , (< __TypeMapper as apikit :: rpc :: RequestParamSign < Json < Params > >> :: Input ,) : apikit :: rpc :: param :: ParamAppender << C as apikit :: rpc :: http_client :: HttpClient > :: ParamBuilder > ,{
            let addr = <S as apikit::rpc::ServerAddressManager>::get_server_addr(
                &self.server_manager,
                <__Server as apikit::ServerDef>::server_name(),
            );
            let path = <FooHttpDef_ as apikit::HandlerDef>::path();
            let url = format!("{addr}{path}");
            let method = <FooHttpDef_ as apikit::HandlerDef>::method();
            <C as apikit::rpc::http_client::HttpClient>::call_service(
                &self.client,
                &url,
                method,
                (param1,),
            )
            .await
        }
        pub async fn sse(
            &self,
            param1: <__TypeMapper as apikit::rpc::RequestParamSign<Json<Params>>>::Input,
        ) -> Result<
            apikit::rpc::sse_client::TypedEventSource<
                <C as apikit::rpc::sse_client::SseClient>::EventSource,
                Result<Event, String>,
            >,
            <C as apikit::rpc::sse_client::SseClient>::Error,
        >
        where
            <C as apikit::rpc::sse_client::SseClient>::EventSource:
                apikit::rpc::sse_client::EventSource<Result<Event, String>>,
            (<__TypeMapper as apikit::rpc::RequestParamSign<Json<Params>>>::Input,):
                apikit::rpc::param::ParamAppender<
                        <C as apikit::rpc::sse_client::SseClient>::ParamBuilder,
                    >,
        {
            let addr = <S as apikit::rpc::ServerAddressManager>::get_server_addr(
                &self.server_manager,
                <__Server as apikit::ServerDef>::server_name(),
            );
            let path = <FooSseDef_ as apikit::HandlerDef>::path();
            let url = format!("{addr}{path}");
            let method = <FooSseDef_ as apikit::HandlerDef>::method();
            <C as apikit::rpc::sse_client::SseClient>::event_source(
                &self.client,
                &url,
                method,
                (param1,),
            )
            .await
        }
        pub async fn ws(
            &self,
            param2: <__TypeMapper as apikit::rpc::RequestParamSign<Json<Params>>>::Input,
        ) -> Result<
            apikit::rpc::ws_client::TypedWebsocket<
                <C as apikit::rpc::ws_client::WsClient>::Websocket,
                <__TypeMapper as apikit::rpc::WebsocketParamSign<WsHandle>>::SendMessage,
                <__TypeMapper as apikit::rpc::WebsocketParamSign<WsHandle>>::ReceiveMessage,
            >,
            <C as apikit::rpc::ws_client::WsClient>::Error,
        >
        where
            <C as apikit::rpc::ws_client::WsClient>::Websocket: apikit::rpc::ws_client::Websocket<
                    <__TypeMapper as apikit::rpc::WebsocketParamSign<WsHandle>>::SendMessage,
                    <__TypeMapper as apikit::rpc::WebsocketParamSign<WsHandle>>::ReceiveMessage,
                >,
            (<__TypeMapper as apikit::rpc::RequestParamSign<Json<Params>>>::Input,):
                apikit::rpc::param::ParamAppender<
                        <C as apikit::rpc::ws_client::WsClient>::ParamBuilder,
                    >,
        {
            let addr = <S as apikit::rpc::ServerAddressManager>::get_server_addr(
                &self.server_manager,
                <__Server as apikit::ServerDef>::server_name(),
            );
            let path = <FooWsDef_ as apikit::HandlerDef>::path();
            let url = format!("{addr}{path}");
            let method = <FooWsDef_ as apikit::HandlerDef>::method();
            <C as apikit::rpc::ws_client::WsClient>::websocket(
                &self.client,
                &url,
                method,
                (param2,),
            )
            .await
        }
    }
    #[doc = r" 模块注释"]
    #[async_trait::async_trait]
    pub trait Foo {
        async fn http(self, params: Json<Params>) -> Result<String, String>;
        async fn sse(self, params: Json<Params>) -> Result<Event, String>;
        async fn ws(self, ws: WsHandle, params: Json<Params>) -> Result<UpgradeResponse, String>;
    }

    /// as
    /// sfs
    #[handler(method=POST, protocol="ws")]
    async fn ws(self, ws: WsHandle, params: Json<Params>) -> Result<UpgradeResponse, String>;

    #[tokio::test]
    async fn a() {
        // let rpc = Rpc(FooClient::new(Client::new(), ServerManager));
        // let mut ws = rpc.0.ws(String::default()).await.unwrap();
        // let message = ws.next_message().await;
        // ws.close_websocket(reqwest_websocket::CloseCode::Normal, Some(""))
        //     .await
        //     .unwrap();
    }
}
